/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2018-2019.
 * Description: rtos_cpu_mode.c
 * Author: zhoumansuo <zhoumansuo@huawei.com>
 * Create: 2018-10-22
 */
#include <mach/uncompress.h>
#include <linux/libfdt.h>

#define ERROR_DEFAULT	0xF1E2D3C4

volatile unsigned int gic_base_addr;

unsigned int phs_addr;
unsigned int reserve_mem_point;

#define GIC_BASE_ADDR	0xf0801000

#define GIC_SEC_OFFSET	0x20

#define TZPC_CTRL   0x804
#define TZPC_ATTR   0x810
#define DMAC_READ   0x70
#define DMAC_WRITE  0x74

#define L2_CTLR			0x03201400
#define L2_ACTLR		0x04000000
#define L2_PFR			0x000009b0
#define	L2_ACTLR2		0x80000000

#define ENABLE_MONITOR		(1 << 5)
#define ENABLE_EVENTBUS	(1 << 6)
#define ENABLE_ECC		(1 << 7)

unsigned int gic_total_num;
unsigned int nr_gic_cpu;

static void set_gicinfo_to_reserve_mem(void)
{
	const unsigned int base = 0;

	phs_addr = base;

	io_write((base + HISI_NONSEC_GICADDR_STORE), gic_base_addr);
	io_write((base + HISI_NONSEC_MAXIRQ_STORE), gic_total_num);
	io_write((base + HISI_NONSEC_NR_GIC_CPU), nr_gic_cpu);
}

static void init_gic_info(void)
{
	void *dtb_address = (void *)__dtb_addr;
	int node, size;
	const void *data = NULL;
	unsigned int n_addr_cells;
	volatile unsigned int gic_secure_cfg;

	gic_base_addr = GIC_BASE_ADDR;
	gic_secure_cfg = ERROR_DEFAULT;

	node = fdt_node_offset_by_prop_value(dtb_address, -1,
		"compatible", "hisilicon,hip04-gic", sizeof("hisilicon,hip04-gic"));
	if (node >= 0) {
		nr_gic_cpu = 16;

		data = fdt_getprop(dtb_address, node, "reg", &size);
		if (data) {
			n_addr_cells = get_addr_cells(dtb_address, node);
			if (n_addr_cells == 1)
				gic_base_addr = fdt32_to_cpu(*(fdt32_t *)data);
			else if (n_addr_cells == 2)
				/*
				 * device space can't exceed 32bit, even if LPAE enable.
				 * LPAE only for memory space.
				 */
				gic_base_addr = (u32)fdt64_to_cpu(*(fdt64_t *)data);
		}

		data = fdt_getprop(dtb_address, node, "max_nr_irqs", &size);
		if (data)
			gic_total_num = fdt32_to_cpu(*(fdt32_t *)data);
		else
			gic_total_num = 512;
	} else {
		nr_gic_cpu = 8;

		hisi_putstr("cannot find gic node, we set it as default\n");
	}

	hisi_putstr("gic base = ");
	print_number(gic_base_addr);
	hisi_putstr("\n");
	hisi_putstr("gic_total_num = ");
	print_number(gic_total_num);
	hisi_putstr("\n");

	node = fdt_node_offset_by_prop_value(dtb_address, -1,
		"compatible", "arm,sp810", sizeof("arm,sp810"));
	if (node >= 0) {
		data = fdt_getprop(dtb_address, node, "gic_secure_cfg", &size);
		if (data)
			gic_secure_cfg = fdt32_to_cpu(*(fdt32_t *)data);
		hisi_putstr("gic_secure_cfg = ");
		print_number(gic_secure_cfg);
		hisi_putstr("\n");
	} else {
		hisi_putstr("cannot find gic_secure_cfg, we set it as default\n");
	}

	set_gicinfo_to_reserve_mem();
}

static void nonsec_reg_set(unsigned int clear_bit, unsigned int set_bit, unsigned int reg_base)
{
	unsigned int reg_value;

	IO_READ(reg_base, reg_value);
	reg_value &= ~clear_bit;
	reg_value |= set_bit;
	io_write(reg_base, reg_value);

	io_write(reserve_mem_point, clear_bit);
	reserve_mem_point += 4;
	io_write(reserve_mem_point, set_bit);
	reserve_mem_point += 4;
	io_write(reserve_mem_point, reg_base);
	reserve_mem_point += 4;
}


static void set_dts_nonsec_regs(void)
{
	int node;
	int size;
	const u32 *data = NULL;
	unsigned int clear_bit = 0, set_bit = 0, reg_base = 0;

	reserve_mem_point = phs_addr + HISI_NONSEC_START_MAGIC_ADDR;
	io_write(reserve_mem_point, NONSEC_START_MAGIC);
	reserve_mem_point += 4;

	node = fdt_node_offset_by_prop_value(__dtb_addr, -1, "compatible",
			"hisi,non_secure", sizeof("hisi,non_secure"));

	while (node >= 0) {
		node = fdt_node_offset_by_prop_value(__dtb_addr, node, "compatible",
				"non_secure,reg_info", sizeof("non_secure,reg_info"));
		if (node < 0)
			break;

		data = fdt_getprop(__dtb_addr, node, "clear_bit", &size);
		if (data && (sizeof(int) == size)) {
			clear_bit = fdt32_to_cpu(*data);

			hisi_putstr("clear_bit = ");
			print_number(clear_bit);
			hisi_putstr("\n");
		}

		data = fdt_getprop(__dtb_addr, node, "set_bit", &size);
		if (data && (sizeof(int) == size)) {
			set_bit = fdt32_to_cpu(*data);

			hisi_putstr("set_bit = ");
			print_number(set_bit);
			hisi_putstr("\n");
		}

		data = fdt_getprop(__dtb_addr, node, "reg_base", &size);
		if (data && (sizeof(int) == size)) {
			reg_base = fdt32_to_cpu(*data);

			hisi_putstr("reg_base = ");
			print_number(reg_base);
			hisi_putstr("\n");
		}

		nonsec_reg_set(clear_bit, set_bit, reg_base);
	}

	io_write(reserve_mem_point, NONSEC_END_MAGIC);
}

void setup_nonsec(void)
{
	hisi_putstr("set non secure\n");
	init_gic_info();
	set_dts_nonsec_regs();
	hisi_putstr("set non secure done\n");
}
